--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Commit d4230105ebea24957fd74ce949613a376cf7995b
Parents : d66d44a
Author : Ivan <ivan@quad4.io>
Signature : Invalid signer <e46112d44649266d71fe2193e00a4710>, author is <ivan@quad4.io>
Date : 2026-07-08T13:13:05-05:00
refactor(meshchat): improve code readability by formatting long lines and adding comments for clarity
Changes
3 files changed, 182 insertions(+), 9 deletions(-)
Diff
diff --git a/meshchatx/meshchat.py b/meshchatx/meshchat.py
index 8644f630..94f534b1 100644
--- a/meshchatx/meshchat.py
+++ b/meshchatx/meshchat.py
@@ -781,11 +781,15 @@ class ReticulumMeshChat:
if config_ok:
try:
reticulum_config_path = self._api_reticulum_config_path()
- if not reticulum_config_path or not os.path.exists(reticulum_config_path):
+ if not reticulum_config_path or not os.path.exists(
+ reticulum_config_path
+ ):
config_ok = False
config_reason = "Reticulum config file not found"
else:
- if not reticulum_config_has_required_sections(reticulum_config_path):
+ if not reticulum_config_has_required_sections(
+ reticulum_config_path
+ ):
config_ok = False
config_reason = "Reticulum config is missing required sections"
except Exception as e:
@@ -803,7 +807,9 @@ class ReticulumMeshChat:
snapshot = self.database.get_database_health_snapshot()
if snapshot.get("quick_check") not in ("ok", "unknown"):
db_ok = False
- db_reason = f"Database quick check returned: {snapshot.get('quick_check')}"
+ db_reason = (
+ f"Database quick check returned: {snapshot.get('quick_check')}"
+ )
except Exception as e:
db_ok = False
db_reason = f"Database check failed: {str(e)}"
@@ -819,13 +825,13 @@ class ReticulumMeshChat:
test_data = "meshchatx_self_test_write_read_verify"
with open(temp_file_path, "w", encoding="utf-8") as f:
f.write(test_data)
-
+
with open(temp_file_path, "r", encoding="utf-8") as f:
read_data = f.read()
-
+
if os.path.exists(temp_file_path):
os.remove(temp_file_path)
-
+
if read_data != test_data:
rw_ok = False
rw_reason = "Read data did not match written data"
@@ -20273,7 +20279,7 @@ def main():
print("\n================================")
print(" System Self-Check Results")
print("================================")
-
+
all_passed = True
labels = {
"stack_up": "Network Stack ",
@@ -20281,7 +20287,7 @@ def main():
"db_good": "Database Connection ",
"read_write_good": "Storage Read/Write ",
}
-
+
for key, name in labels.items():
check = results.get(key, {"status": "failed", "reason": "No result"})
if check["status"] == "ok":
@@ -20290,7 +20296,7 @@ def main():
all_passed = False
reason = check.get("reason") or "Unknown error"
print(f"[FAILED] {name} - Reason: {reason}")
-
+
print("================================")
if all_passed:
print("Status: SUCCESS (All checks passed)")
diff --git a/tests/backend/conftest.py b/tests/backend/conftest.py
index 4f2b38eb..fe56bac2 100644
--- a/tests/backend/conftest.py
+++ b/tests/backend/conftest.py
@@ -2,6 +2,10 @@
import asyncio
import os
+
+# Disable Landlock sandbox globally during backend testing to prevent process lockdown and PermissionError crashes.
+os.environ["MESHCHAT_LANDLOCK"] = "0"
+
import socket
from contextlib import ExitStack
from unittest.mock import MagicMock, patch
diff --git a/tests/backend/test_self_check_cli.py b/tests/backend/test_self_check_cli.py
new file mode 100644
index 00000000..3662e495
--- /dev/null
+++ b/tests/backend/test_self_check_cli.py
@@ -0,0 +1,163 @@
+# SPDX-License-Identifier: 0BSD
+
+import shutil
+import tempfile
+import sys
+from unittest.mock import MagicMock, patch
+
+import pytest
+import RNS
+
+from meshchatx.meshchat import ReticulumMeshChat, main
+
+
+@pytest.fixture
+def temp_dir():
+ dir_path = tempfile.mkdtemp()
+ yield dir_path
+ shutil.rmtree(dir_path)
+
+
+@pytest.fixture
+def mock_rns():
+ # Save the real identity class to use as base for our mock class
+ real_identity_class = RNS.Identity
+
+ class MockIdentityClass(real_identity_class):
+ def __init__(self, *args, **kwargs):
+ self.hash = b"test_hash_32_bytes_long_01234567"
+ self.hexhash = self.hash.hex()
+
+ def get_private_key(self):
+ return b"test_private_key"
+
+ def load(self, *args, **kwargs):
+ pass
+
+ def load_private_key(self, *args, **kwargs):
+ pass
+
+ with (
+ patch("RNS.Reticulum") as mock_reticulum,
+ patch("RNS.Transport") as mock_transport,
+ patch("RNS.Identity", MockIdentityClass),
+ patch("threading.Thread"),
+ patch("LXMF.LXMRouter"),
+ patch.object(ReticulumMeshChat, "announce_loop", return_value=None),
+ patch.object(
+ ReticulumMeshChat,
+ "announce_sync_propagation_nodes",
+ return_value=None,
+ ),
+ patch.object(ReticulumMeshChat, "crawler_loop", return_value=None),
+ patch.object(ReticulumMeshChat, "auto_backup_loop", return_value=None),
+ patch.object(
+ ReticulumMeshChat,
+ "send_config_to_websocket_clients",
+ return_value=None,
+ ),
+ ):
+ mock_id_instance = MockIdentityClass()
+
+ with (
+ patch.object(MockIdentityClass, "from_file", return_value=mock_id_instance),
+ patch.object(MockIdentityClass, "recall", return_value=mock_id_instance),
+ patch.object(
+ MockIdentityClass,
+ "from_bytes",
+ return_value=mock_id_instance,
+ ),
+ ):
+ yield {
+ "Reticulum": mock_reticulum,
+ "Transport": mock_transport,
+ "Identity": MockIdentityClass,
+ "id_instance": mock_id_instance,
+ }
+
+
+def test_self_check_cli_success(mock_rns, temp_dir):
+ """Test that self-check CLI argument prints results and exits with 0 on success."""
+ mock_results = {
+ "stack_up": {"status": "ok", "reason": ""},
+ "config_good": {"status": "ok", "reason": ""},
+ "db_good": {"status": "ok", "reason": ""},
+ "read_write_good": {"status": "ok", "reason": ""},
+ }
+
+ with (
+ patch("meshchatx.meshchat.ReticulumMeshChat") as mock_app_class,
+ patch("meshchatx.src.backend.identity_context.Database"),
+ patch("meshchatx.src.backend.identity_context.ConfigManager"),
+ patch("aiohttp.web.run_app"),
+ patch("sys.argv", ["meshchat.py", "--storage-dir", temp_dir, "--self-check"]),
+ ):
+ mock_app_instance = mock_app_class.return_value
+ mock_app_instance.run_self_test = MagicMock(return_value=mock_results)
+
+ with pytest.raises(SystemExit) as excinfo:
+ main()
+
+ assert excinfo.value.code == 0
+ mock_app_instance.run_self_test.assert_called_once()
+
+
+def test_self_check_cli_failure(mock_rns, temp_dir):
+ """Test that self-check CLI argument prints results and exits with 1 on failure."""
+ mock_results = {
+ "stack_up": {
+ "status": "failed",
+ "reason": "Reticulum stack is not initialized",
+ },
+ "config_good": {"status": "ok", "reason": ""},
+ "db_good": {"status": "failed", "reason": "Database check failed"},
+ "read_write_good": {"status": "ok", "reason": ""},
+ }
+
+ with (
+ patch("meshchatx.meshchat.ReticulumMeshChat") as mock_app_class,
+ patch("meshchatx.src.backend.identity_context.Database"),
+ patch("meshchatx.src.backend.identity_context.ConfigManager"),
+ patch("aiohttp.web.run_app"),
+ patch("sys.argv", ["meshchat.py", "--storage-dir", temp_dir, "--self-check"]),
+ ):
+ mock_app_instance = mock_app_class.return_value
+ mock_app_instance.run_self_test = MagicMock(return_value=mock_results)
+
+ with pytest.raises(SystemExit) as excinfo:
+ main()
+
+ assert excinfo.value.code == 1
+ mock_app_instance.run_self_test.assert_called_once()
+
+
+def test_self_check_env_var_success(mock_rns, temp_dir):
+ """Test that MESHCHAT_SELF_CHECK env var triggers self-check and exits with 0 on success."""
+ mock_results = {
+ "stack_up": {"status": "ok", "reason": ""},
+ "config_good": {"status": "ok", "reason": ""},
+ "db_good": {"status": "ok", "reason": ""},
+ "read_write_good": {"status": "ok", "reason": ""},
+ }
+
+ env = {
+ "MESHCHAT_SELF_CHECK": "true",
+ "MESHCHAT_STORAGE_DIR": temp_dir,
+ }
+
+ with (
+ patch("meshchatx.meshchat.ReticulumMeshChat") as mock_app_class,
+ patch("meshchatx.src.backend.identity_context.Database"),
+ patch("meshchatx.src.backend.identity_context.ConfigManager"),
+ patch("aiohttp.web.run_app"),
+ patch.dict("os.environ", env),
+ patch("sys.argv", ["meshchat.py"]),
+ ):
+ mock_app_instance = mock_app_class.return_value
+ mock_app_instance.run_self_test = MagicMock(return_value=mock_results)
+
+ with pytest.raises(SystemExit) as excinfo:
+ main()
+
+ assert excinfo.value.code == 0
+ mock_app_instance.run_self_test.assert_called_once()
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────